<template>
  <div class="login">
    <div ref="container" class="container">
      <canvas id="canvas" ref="canvas"></canvas>
    </div>
    <div class="box">
      <el-card class="box-card">
        <template #header>
          <div class="card-header">
            <span>登录</span>
          </div>
        </template>
        <el-form
          ref="ruleFormRef"
          :model="ruleForm"
          status-icon
          :rules="rules"
          label-width="60px"
          class="demo-ruleForm"
          >1111
          <el-form-item label="用户名" prop="userName">
            <el-input
              v-model="ruleForm.userName"
              type="text"
              autocomplete="off"
            />
          </el-form-item>
          <el-form-item label="密码" prop="password">
            <el-input
              v-model="ruleForm.password"
              type="password"
              autocomplete="off"
            />
          </el-form-item>
          <el-form-item label="验证码" prop="verify">
            <div :style="{ display: 'flex' }">
              <el-input
                v-model="ruleForm.verify"
                type="text"
                autocomplete="off"
                :style="{ width: '240px', height: '30px' }"
              />
              <canvas
                ref="verify"
                :width="140"
                :height="30"
                @click="handleDraw"
              ></canvas>
            </div>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="submitForm(ruleFormRef)"
              >登录</el-button
            >
          </el-form-item>
        </el-form>
      </el-card>
    </div>
  </div>
</template>

<script setup>
import useRuleLogin from "../hooks/login";
import { onMounted, ref, reactive, toRefs } from "vue";
const { ruleFormRef, ruleForm, rules, submitForm } = useRuleLogin();
const container = ref();
const canvas = ref();
const ctx = ref();
const w = ref(1000);
const h = ref(800);
const hue = 217,
  stars = ref([]),
  maxStars = 1300;

const canvas2 = ref();
const ctx2 = ref();

const half = ref(0);
const gradient2 = ref();
const verify = ref(null);
const state = reactive({
  pool: "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", // 字符串
  width: 120,
  height: 40,
  imgCode: "",
});
const random = (min, max) => {
  if (!max) {
    max = min;
    min = 0;
  }

  if (min > max) {
    let hold = max;
    max = min;
    min = hold;
  }
  return Math.floor(Math.random() * (max - min + 1)) + min;
};

/**
 * 计算星星移动范围
 * @param x
 * @param y
 */
const maxOrbit = (x, y) => {
  const max = Math.max(x, y),
    diameter = Math.round(Math.sqrt(max * max + max * max));
  return diameter / 2;
  //星星移动范围，值越大范围越小，
};

/**
 * 绘制星星
 */
const drawStar = (ctx, star) => {
  const x = Math.sin(star.timePassed) * star.orbitRadius + star.orbitX,
    y = Math.cos(star.timePassed) * star.orbitRadius + star.orbitY,
    twinkle = random(10);

  if (twinkle === 1 && star.alpha > 0) {
    star.alpha -= 0.05;
  } else if (twinkle === 2 && star.alpha < 1) {
    star.alpha += 0.05;
  }

  ctx.globalAlpha = star.alpha;
  ctx.drawImage(
    canvas2.value,
    x - star.radius / 2,
    y - star.radius / 2,
    star.radius,
    star.radius
  );
  star.timePassed += star.speed;
};

/**
 * 星星的旋转动画
 */
const animation = () => {
  ctx.value.globalCompositeOperation = "source-over";
  ctx.value.globalAlpha = 0.5; //尾巴
  ctx.value.fillStyle = "hsla(" + hue + ", 64%, 6%, 2)";
  ctx.value.fillRect(0, 0, w.value, h.value);

  ctx.value.globalCompositeOperation = "lighter";
  for (let i = 1, l = stars.value.length; i < l; i++) {
    drawStar(ctx.value, stars.value[i]);
  }

  window.requestAnimationFrame(animation);
};

/**
 * 产生星星
 */
const generateStar = () => {
  const orbitRadius = random(maxOrbit(w.value, h.value));
  // 星星大小
  const radius = random(60, orbitRadius) / 8;
  const orbitX = w.value / 2;
  const orbitY = h.value / 2;
  const timePassed = random(0, maxStars);
  // 星星移动速度
  const speed = random(orbitRadius) / 50000;
  const alpha = random(2, 10) / 10;
  const star = {
    orbitX,
    orbitY,
    orbitRadius,
    radius,
    timePassed,
    speed,
    alpha,
  };
  return star;
};

/**
 * 初始化星星的数量
 */
const initStar = () => {
  ctx.value = canvas.value.getContext("2d");
  w.value = container.value?.clientWidth;
  h.value = container.value?.clientHeight;
  canvas.value.width = container.value?.clientWidth || 1000;
  canvas.value.height = container.value?.clientHeight || 800;
  canvas2.value = document.createElement("canvas");
  ctx2.value = canvas2.value.getContext("2d");
  canvas2.value.width = 100;
  canvas2.value.height = 100;
  half.value = canvas2.value.width / 2;
  gradient2.value = ctx2.value.createRadialGradient(
    half.value,
    half.value,
    0,
    half.value,
    half.value,
    half.value
  );
  gradient2.value.addColorStop(0.025, "#CCC");
  gradient2.value.addColorStop(0.1, "hsl(" + hue + ", 61%, 33%)");
  gradient2.value.addColorStop(0.25, "hsl(" + hue + ", 64%, 6%)");
  gradient2.value.addColorStop(1, "transparent");
  ctx2.value.fillStyle = gradient2.value;
  ctx2.value.beginPath();
  ctx2.value.arc(half.value, half.value, half.value, 0, Math.PI * 2);
  ctx2.value.fill();
  for (let i = 0; i < maxStars; i++) {
    const star = generateStar();
    stars.value.push(star);
  }
};
const handleDraw = () => {
  state.imgCode = draw();
};
const randomNum = (min, max) => {
  return parseInt(Math.random() * (max - min) + min);
};
// 随机颜色
const randomColor = (min, max) => {
  const r = randomNum(min, max);
  const g = randomNum(min, max);
  const b = randomNum(min, max);
  return `rgb(${r},${g},${b})`;
};

// 绘制图片
const draw = () => {
  // 3.填充背景颜色，背景颜色要浅一点
  const ctx = verify.value.getContext("2d");
  // 填充颜色
  ctx.fillStyle = randomColor(180, 230);
  // 填充的位置
  ctx.fillRect(0, 0, state.width, state.height);
  // 定义paramText
  let imgCode = "";
  // 4.随机产生字符串，并且随机旋转
  for (let i = 0; i < 4; i++) {
    // 随机的四个字
    const text = state.pool[randomNum(0, state.pool.length)];
    imgCode += text;
    // 随机的字体大小
    const fontSize = randomNum(18, 40);
    // 字体随机的旋转角度
    const deg = randomNum(-30, 30);
    /*
     * 绘制文字并让四个文字在不同的位置显示的思路 :
     * 1、定义字体
     * 2、定义对齐方式
     * 3、填充不同的颜色
     * 4、保存当前的状态（以防止以上的状态受影响）
     * 5、平移translate()
     * 6、旋转 rotate()
     * 7、填充文字
     * 8、restore出栈
     * */
    ctx.font = fontSize + "px Simhei";
    ctx.textBaseline = "top";
    ctx.fillStyle = randomColor(80, 150);
    /*
     * save() 方法把当前状态的一份拷贝压入到一个保存图像状态的栈中。
     * 这就允许您临时地改变图像状态，
     * 然后，通过调用 restore() 来恢复以前的值。
     * save是入栈，restore是出栈。
     * 用来保存Canvas的状态。save之后，可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。 restore：用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。
     *
     * */
    ctx.save();
    ctx.translate(30 * i + 15, 15);
    ctx.rotate((deg * Math.PI) / 180);
    // fillText() 方法在画布上绘制填色的文本。文本的默认颜色是黑色。
    // 请使用 font 属性来定义字体和字号，并使用 fillStyle 属性以另一种颜色/渐变来渲染文本。
    // context.fillText(text,x,y,maxWidth);
    ctx.fillText(text, -15 + 5, -15);
    ctx.restore();
  }
  // 5.随机产生5条干扰线,干扰线的颜色要浅一点
  for (let i = 0; i < 5; i++) {
    ctx.beginPath();
    ctx.moveTo(randomNum(0, state.width), randomNum(0, state.height));
    ctx.lineTo(randomNum(0, state.width), randomNum(0, state.height));
    ctx.strokeStyle = randomColor(180, 230);
    ctx.closePath();
    ctx.stroke();
  }
  // 6.随机产生40个干扰的小点
  for (let i = 0; i < 40; i++) {
    ctx.beginPath();
    ctx.arc(
      randomNum(0, state.width),
      randomNum(0, state.height),
      1,
      0,
      2 * Math.PI
    );
    ctx.closePath();
    ctx.fillStyle = randomColor(150, 200);
    ctx.fill();
  }
  return imgCode;
};
onMounted(() => {
  initStar();
  animation();
  state.imgCode = draw();
});
</script>

<style lang="less" scoped>
.container {
  width: 100%;
  height: 100%;
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
}

#canvas {
  width: 100%;
  height: 100%;
  z-index: -1;
  position: absolute;
  overflow: hidden;
}

.login {
  width: 100%;
  height: 100%;
}
.box {
  width: 482px;
  height: 300px;
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background: #fff !important;
  z-index: 100;
}
.el-card {
  height: 300px;
  background: #fff !important;
}
.card-header {
  text-align: center;
}

.text {
  font-size: 14px;
}

.item {
  margin-bottom: 18px;
}

.box-card {
  width: 480px;
  background: #fff !important;
}
</style>
